package zdream.control.graphics;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Function;

import zdream.control.assets.FoeDescription;
import zdream.control.world.MFoe;
import zdream.control.world.Ticker;

/**
 * @author Zdream
 * @date 2023-12-29
 * @since 0.0.1
 */
public abstract class AbstractLevelGraphics {
	/**
	 * 如果像跑 case 这种不需要绘制的, enable = false 就行.
	 * 虽然不需要绘制, 但是每个组件的 tick 还是要调用的.
	 */
	public boolean enabled;

	public void draw(Ticker time) {
		if (!enabled) {
			return;
		}

		clearCanvas();

		// 画所有 Foe / TMX / Profile
		drawWorld(time);

		// 画 Profile
		drawUI(time);
	}

	protected abstract void clearCanvas();

	/* **********
	 * 世界绘制 *
	 ********** */
	public final List<IPainter> worldPainters = new ArrayList<>();
	private IPainter[] tmpWorlds;
	public Function<String, FoeDescription> descriptionFinder = null;

	public BooleanSupplier addWorldPainter(MFoe foe) {
		FoeDescription description = descriptionFinder.apply(foe.foeName());
		if (description == null) {
			System.err.println("无法找到 " + foe.foeName() + " 的绘画参数, 无法生成 Painter");
			return () -> false;
		}
		IPainter painter = createPainterByDescription(foe, description);
		if (painter == null) {
			return () -> false;
		}
		BooleanSupplier cleaner = addWorldPainter(painter);
		return () -> {
			boolean success = cleaner.getAsBoolean();
			if (success) {
				painter.dispose();
			}
			return success;
		};
	}

	protected abstract IPainter createPainterByDescription(MFoe foe, FoeDescription description);

	public BooleanSupplier addWorldPainter(IPainter painter) {
		worldPainters.add(painter);
		tmpWorlds = null;
		painter.onApply();
		return () -> removeWorldPainter(painter);
	}

	public boolean removeWorldPainter(IPainter painter) {
		painter.onRemove();
		tmpWorlds = null;
		return worldPainters.remove(painter);
	}

	protected void drawWorld(Ticker time) {
		if (tmpWorlds == null) {
			tmpWorlds = worldPainters.toArray(new IPainter[0]);
			Arrays.sort(tmpWorlds);
		}

		for (IPainter painter : tmpWorlds) {
			if (!painter.isEnable()) {
				continue;
			}
			if (!painter.hasLoaded()) {
				painter.load();
			}
			painter.draw(time);
		}
	}

	/* **********
	 * UI 绘制  *
	 ********** */
	public final List<IPainter> uiPainters = new ArrayList<>();
	private IPainter[] tmpUIs;

	public BooleanSupplier addUIPainter(IPainter painter) {
		uiPainters.add(painter);
		tmpUIs = null;
		painter.onApply();
		return () -> removeUIPainter(painter);
	}

	public boolean removeUIPainter(IPainter painter) {
		painter.onRemove();
		tmpUIs = null;
		return uiPainters.remove(painter);
	}

	protected void drawUI(Ticker time) {
		if (tmpUIs == null) {
			tmpUIs = uiPainters.toArray(new IPainter[0]);
			Arrays.sort(tmpUIs);
		}

		for (IPainter painter : tmpUIs) {
			if (!painter.isEnable()) {
				continue;
			}
			if (!painter.hasLoaded()) {
				painter.load();
			}
			painter.draw(time);
		}
	}
}
